<!DOCTYPE html>
<html>

<head>
  <title>用户B-语音通话</title>
  <style>
    body {
      margin: 0;
      padding: 0;
      width: 100%;
      height: 100vh;
      display: flex;
      justify-content: center;
    }

    .box {
      display: flex;
      flex-direction: column;
      align-items: center;
    }

    .box:first-child {
      margin-right: 20px;
    }

    .container {
      width: 370px;
      height: 580px;
      background-color: #f5f5f5;
      margin-bottom: 20px;
      position: relative;
    }

    button {
      width: 100px;
      height: 40px;
      cursor: pointer;
      background-color: #fff;
      border: 1px solid #ccc;
      color: #555;
      font-size: 15px;
    }

    button:first-child {
      margin-right: 20px;
    }

    video {
      width: 100%;
      height: 100%;
      object-fit: cover;
    }

    .container-small {
      width: 120px;
      height: 160px;
      position: absolute;
      right: 0;
      bottom: 0;
      z-index: 1;
    }

    .layer {
      width: 100%;
      height: 100%;
      position: absolute;
      z-index: 2;
      top: 0;
      left: 0;
      background-color: rgba(0, 0, 0, 0.8);
      color: #fff;
      display: flex;
      flex-direction: column;
      align-items: center;
      display: none;
    }

    .layer__text {
      margin-top: 150px;
      margin-bottom: 260px;
    }

    .layer__btns {
      display: flex;
    }

    .layer__btns .layer__btn:last-child {
      margin-left: 100px;
    }

    .layer__btn {
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-items: center;
      font-size: 12px;
      cursor: pointer;
    }

    .layer__btn img {
      border-radius: 50%;
      background-color: #fff;
      margin-bottom: 8px;
    }
  </style>
</head>

<body>
  <div class="box">
    <h1>用户B</h1>
    <div class="container">
      <video id="videoB"></video>
      <div class="container-small">
        <video id="videoBSmall"></video>
      </div>
      <div id="layerB" class="layer">
        <div class="layer__text">用户A邀请你视频通话...</div>
        <div class="layer__btns">
          <div id="rejectBtn" class="layer__btn">
            <img
              src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6400a568de2e4a57b04e684be4004d52~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=48&h=48&s=870&e=png&a=1&b=f53c36" />
            <span>挂断</span>
          </div>
          <div id="acceptBtn" class="layer__btn">
            <img
              src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/11f90e68b4074fd899d3d512ef9bba5f~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=48&h=48&s=881&e=png&a=1&b=47de66" />
            <span>接听</span>
          </div>
        </div>
      </div>
    </div>
    <button id="hangupBtn">挂断</button>
  </div>
  <script type="module">
    import { io } from "https://cdn.socket.io/4.7.5/socket.io.esm.min.js";
    document.addEventListener('DOMContentLoaded', () => {
      const socket = io('http://localhost:3000/');
      const roomId = '123456';
      socket.on('connectionSuccess', () => {
        console.log('连接成功');
        socket.emit('joinRoomEvent', roomId);
      });
      const layerB = document.getElementById('layerB');
      const videoB = document.getElementById('videoB');
      const videoBSmall = document.getElementById('videoBSmall');
      let streamB = null;
      let calling = false;
      let communicating = false;

      // 监听申请通话的事件
      socket.on('callEvent', async () => {
        calling = true;
        layerB.style.display = 'flex';
        const stream = await getLocalStream();
        videoB.srcObject = stream;
        videoB.play();
        streamB = stream;
      });

      const acceptBtn = document.getElementById('acceptBtn');
      // 同意接听
      acceptBtn.addEventListener('click', () => {
        socket.emit('acceptCallEvent', roomId);
      });
      const rejectBtn = document.getElementById('rejectBtn');
      // 拒绝接听
      rejectBtn.addEventListener('click', () => {
        socket.emit('end', roomId);
        hangupCall();
      });
      let peer = null;
      // 监听offer事件
      socket.on('offerEvent', async offer => {
        peer = new RTCPeerConnection();
        for (const track of streamB.getTracks()) {
          peer.addTrack(track, streamB);
        }
        peer.onicecandidate = event => {
          if (event.candidate) {
            socket.emit('candidateEvent', { roomId, candidate: event.candidate });
          }
        }
        peer.ontrack = event => {
          calling = false;
          layerB.style.display = 'none';
          communicating = true;
          videoBSmall.srcObject = event.streams[0];
          setTimeout(() => {
            videoBSmall.play();
          }, 100);
        }

        // 设置"远端"描述的offer
        await peer.setRemoteDescription(offer);
        // 生成answer
        const answer = await peer.createAnswer();
        // 设置本地描述的answer
        await peer.setLocalDescription(answer);
        // 将answer发送给对方
        socket.emit('answerEvent', { roomId, answer });
      });
      socket.on('candidateEvent', async candidate => {
        await peer.addIceCandidate(candidate);
      });

      // 监听挂断事件
      socket.on('end', () => {
        window.alert("对方已挂断")
        hangupCall();
      });
      //监听取消呼叫
      socket.on('cancelCallEvent', () => {
        alert("对方已取消呼叫")
        hangupCall();
      });
      // 挂断按钮
      const hangupBtn = document.getElementById('hangupBtn');
      hangupBtn.addEventListener('click', () => {
        if (communicating) {
          hangupCall();
          socket.emit('end', roomId);
        }
      });

      function hangupCall() {
        if (peer) {
          peer.close();
          peer = null;
        }
        if (streamB) {
          streamB.getTracks().forEach(track => track.stop());
          streamB = null;
        }
        videoB.srcObject = null;
        videoBSmall.srcObject = null;
        communicating = false;
        calling = false;
        layerB.style.display = 'none';
      }

      async function getLocalStream() {
        const stream = await navigator.mediaDevices.getUserMedia({
          audio: true,
          video: true
        });
        return stream;
      }
    });
  </script>
</body>

</html>